Multi-Developer Access with Least Privilege
Prerequisites
| Requirement | Details |
|---|---|
| Access Level: | Root or sudo privileges. |
| Software: | SSH + OpenSSH server. |
| Knowledge: | User/group management, /etc/passwd, sudoers. |
| Setup: | WordPress installed in /var/www/html or /srv/wordpress/. |
Why and When
- Why it matters: Prevent accidental or malicious changes outside project scope.
- Why this approach: A secure setup for multiple developers on one VPS.
- When to use it: Onboarding new devs, agencies, contractors.
Core Principles of Least Privilege
- Separation of duties – no shared accounts.
- Group-based access – devs only access project files.
- No root for devs – only lead/admin can sudo.
- Auditability – track who did what.
- Temporary access – auto-expire contractor accounts.
- Key-based auth – avoid weak passwords.
Step-by-Step Implementation
Create a group for developers
sudo groupadd wpdevs
getent group wpdevs
Output:
wpdevs:x:1005:
Add developers to group
sudo useradd -m -s /bin/bash -G wpdevs dev1
sudo useradd -m -s /bin/bash -G wpdevs dev2
id dev1
Output:
uid=1001(dev1) gid=1001(dev1) groups=1001(dev1),1005(wpdevs)
Assign WordPress ownership to web server + dev group
sudo chown -R www-data:wpdevs /var/www/html
sudo chmod -R 775 /var/www/html
Output:
drwxrwxr-x 5 www-data wpdevs 4096 Sep 27 10:00 html
Explanation: Web server = www-data, devs = wpdevs.
Restrict sudo to only lead developer
sudo visudo
Add:
dev1 ALL=(ALL) ALL
%wpdevs ALL=(ALL) NOPASSWD: /usr/bin/wp
Explanation:
dev1= lead dev → full sudo.- Other devs (
wpdevs) can only runwpCLI.
Restrict contractors with expiry
sudo useradd -m -e 2025-12-31 -G wpdevs contract1
chage -l contract1 | grep "Account expires"
Output:
Account expires : Dec 31, 2025
Enforce no-login for service accounts
sudo usermod -s /usr/sbin/nologin www-data
grep www-data /etc/passwd
Output:
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
Use ACLs for fine-grained access (optional)
sudo setfacl -m u:dev2:rwx /var/www/html/wp-content/themes
getfacl /var/www/html/wp-content/themes | grep dev2
Output:
user:dev2:rwx
Track current sessions
w
Output:
USER TTY FROM LOGIN@ WHAT
dev1 pts/0 192.168.1.10 10:30 -bash
View login history
last -n 3
Output:
dev2 pts/1 192.168.1.20 Fri Sep 27 09:00 still logged in
Check privileges of devs
sudo -l -U dev2
Output:
User dev2 may run the following commands:
(ALL : ALL) /usr/bin/wp
WordPress-Specific Access Scenarios
| Scenario | Setup | Benefit |
|---|---|---|
| Theme developer | ACL → rwx on wp-content/themes | Dev can edit only themes |
| Plugin dev | ACL → rwx on wp-content/plugins | Prevents touching core WP |
| Media manager | Group access only to uploads/ | Contractor can upload media |
| Lead dev | Full sudo via visudo | Handles upgrades & server restarts |
| CI/CD bot | SFTP chroot + wp CLI | Automated deploys, no shell access |
Best Practices
- Use group-based permissions for simplicity.
- Give least possible sudo rules (e.g., only
systemctl restart nginx). - Regularly audit with
id,groups,w,last. - Rotate contractor accounts with expiry (
e). - Log and monitor
/var/log/auth.logfor sudo activity. - Disable root SSH login, enforce sudo.
- Use key-based SSH for all dev accounts.
Quick Lab
# Create group
sudo groupadd wpdevs
# Add devs
sudo useradd -m -s /bin/bash -G wpdevs dev1
sudo useradd -m -s /bin/bash -G wpdevs dev2
# Assign WordPress ownership
sudo chown -R www-data:wpdevs /var/www/html
sudo chmod -R 775 /var/www/html
# Verify access
id dev1
ls -ld /var/www/html
Expected Highlights:
uid=1001(dev1) gid=1001(dev1) groups=1001(dev1),1005(wpdevs)
drwxrwxr-x 5 www-data wpdevs 4096 Sep 27 10:00 /var/www/html
Cheat Sheet
| Command | Purpose |
|---|---|
groupadd wpdevs | Create developer group |
useradd -G wpdevs dev1 | Add developer |
chown -R www-data:wpdevs /var/www/html | Set WP ownership |
chmod -R 775 /var/www/html | Allow group writes |
visudo | Configure sudo rules |
useradd -e 2025-12-31 contract1 | Expiring contractor |
setfacl -m u:dev2:rwx path | Fine-grained access |
w | View current sessions |
last | View login history |
sudo -l -U user | Check sudo rights |
Mini Quiz
- Why should each developer get a separate account instead of sharing one?
- What is the benefit of putting WordPress files under
www-data:wpdevswith775permissions? - How do you ensure contractors lose access after a project ends?
- Which command checks if a developer has sudo rights?
- Why is it dangerous to give all developers full sudo access?